#include <platform.h>
#include <infommu.h>
#include <hallink.h>

/*********************************************************************************
 * 
 * platform_init - 硬件平台初始化
 * 
 * DESCRIPTION
 */

void platform_init (void)
    {
    s3c2440_mmu_init ();         /* 初始化s3c2440的MMU */
    s3c2440_intvector_init ();   /* 初始化s3c2440的中断向量 */
    return;
    }

/* ================================= 初始化MMU ================================= */

/*********************************************************************************
 * 
 * s3c2440_mmu_init - 初始化s3c2440的MMU
 * 
 * DESCRIPTION
 */

LKHEAD_T void s3c2440_mmu_init (void)
    {
    uint_t  i;
    uint_t  paddr = 0;
    u32_t   pgdsc = 0;
    u32_t * pgdiraddr = (u32_t *) MMU_PAGE_TLB_ADDR;  /* page direct address */

    /* 初始化 这 MMU_PAGE_TLB_SIZE(4096) 个页表项 */

    for (i = 0; i < MMU_PAGE_TLB_SIZE; i++)
        {
        pgdsc = paddr | MMU_PTE_SECT_AP | MMU_PTE_SECT_DOMAIN | MMU_PTE_SECT_NOCW | MMU_PTE_SECT_BIT;
        pgdiraddr [i] = pgdsc;
        paddr += 0x100000;   /* 4G = 4 * 1024 * 1024 * 1024 = 4096 * 0x100000 */
        }

    pgdiraddr[0] = SDRAM_MAPVECTPHY_ADDR | MMU_PTE_SECT_AP | MMU_PTE_SECT_DOMAIN |
                   MMU_PTE_SECT_NOCW | MMU_PTE_SECT_BIT;

    s3c2440_mmu_set_tblbass (MMU_PAGE_TLB_ADDR); /* 设置 一级页表基地址 (16KB对齐) */
    s3c2440_mmu_set_domain (~0);                 /* MMU访问权限设置为 管理类型权限  */
    s3c2440_mmu_invalid_dicache ();              /* 设置 CACHE (指令和数据) 和 TLB (指令和数据) 数据无效 */
    s3c2440_mmu_set_enable ();                   /* 使能MMU */
    
    return;
    }

/*********************************************************************************
 * 
 * s3c2440_mmu_set_tblbass - 设置 一级页表基地址 (16KB对齐)
 * 
 * DESCRIPTION
 */

LKHEAD_T void s3c2440_mmu_set_tblbass 
    (
    u32_t tblphyaddr
    )
    {
    __asm__ __volatile__
        (

        /* 把 [tbass] 关联的寄存器中的值写入 CP15的c2寄存器 中 */

        "mcr p15, 0, %[tbass], c2, c0, 0 \n\t"
        :

        /* 分配一个通用寄存器名字为[tbass]，在其中写入tblphyaddr的内容，(由GCC完成) */

        : [tbass] "r"(tblphyaddr)
        : "cc", "memory"                   /* 告诉GCC哪些改变了 */
        );

    return;
    }

/*********************************************************************************
 * 
 * s3c2440_mmu_set_domain - 设置 16个域的访问权限
 * 
 * DESCRIPTION
 */

LKHEAD_T void s3c2440_mmu_set_domain
    (
    u32_t domain
    )
    {
    __asm__ __volatile__
        (

        /* 把 [domval] 关联的寄存器中的值写入 CP15的c3寄存器 中 */

        "mcr p15, 0, %[domval], c3, c0, 0 \n\t"
        :

        /* 分配一个通用寄存器名字为[domval]，在其中写入domain的内容，(由GCC完成) */

        : [domval] "r"(domain)
        : "cc", "memory"                    /* 告诉GCC哪些改变了 */
        );

    return;    
    }

/*********************************************************************************
 * 
 * s3c2440_mmu_invalid_dicache - 设置 CACHE (指令和数据) 和 TLB (指令和数据) 数据无效
 * 
 * DESCRIPTION
 */

LKHEAD_T void s3c2440_mmu_invalid_dicache (void)
    {
    __asm__ __volatile__
        (
        "mov r0, #0 \n\t"
        "mcr p15, 0, r0, c7, c7, 0 \n\t"    /* 指令CACHE 和 数据CACHE 中的数据标记为无效 */
        "mcr p15, 0, r0, c7, c10, 4 \n\t"   /* 清空写缓冲区 (将写缓冲区中的内容全部写回到寄存器中) */
        "mcr p15, 0, r0, c8, c7, 0 \n\t"    /* 指令TLB 和 数据TLB 中的数据标记为无效 */
        :
        :
        : "cc", "memory", "r0"              /* 告诉GCC哪些改变了 */
        );

    return; 
    }

/*********************************************************************************
 * 
 * s3c2440_mmu_set_enable - 使能MMU
 * 
 * DESCRIPTION
 */

LKHEAD_T void s3c2440_mmu_set_enable (void)
    {
    __asm__ __volatile__
        (
        "mrc p15, 0, r0, c1, c0, 0 \n\t"    /* 将CP15的C1寄存器中的值读取到r0寄存器中 */
        "orr r0, #1 \n\t"                   /* 改变r0寄存器中的第0位的数值为1 */           
        "mcr p15, 0, r0, c1, c0, 0 \n\t"    /* 将r0寄存器中的数值写入CP15的C1寄存器 (使能MMU) */
 
        /* 填充 CPU 流水线 */

        "nop \n\t"
        "nop \n\t"
        "nop \n\t"      
        "nop \n\t"
        "nop \n\t"
        "nop \n\t"   
        "nop \n\t"                       
        :
        :
        : "cc", "memory", "r0"              /* 告诉GCC哪些改变了 */
        );

    return; 
    }

/* =============================== 初始化中断向量 =============================== */

/*********************************************************************************
 * 
 * s3c2440_intvector_init - 初始化s3c2440的中断向量
 * 
 * DESCRIPTION
 */

LKHEAD_T void s3c2440_intvector_init (void)
    {
        
    /* 初始化s3c2440的中断向量 */

    s3c2440_intvector_copy ();
    }

/*********************************************************************************
 * 
 * s3c2440_intvector_copy - 初始化s3c2440的中断向量
 * 
 * DESCRIPTION
 */

LKHEAD_T void s3c2440_intvector_copy (void)
    {
    uint_t  i;
    u32_t * src_s_addr = (u32_t *) (&_int_vectors);             /* 访问汇编标号需要加 & */
    u32_t * src_e_addr = (u32_t *) (&__end_os_hal_vector);

    u32_t * dest_addr  = (u32_t *) (CPU_INTVECTOR_VIRADDR);     /* CPU中断向量表虚拟地址 (0) */

    /* 清0这块内存 (大小4096字节) (虚拟地址0 对应 物理地址0x30000000) */

    for (i = 0; i < 4096; i++)
        {
        dest_addr[i] = 0;
        }

    /* 将 原中断向量表里的内容 搬移到 虚拟地址为0的地址空间 */

    for (; src_s_addr < src_e_addr; src_s_addr++, dest_addr++)
        {
        *dest_addr = *src_s_addr;
        }

    return;
    }
